package com.gonsin.gview.logic.db;

import com.mysql.cj.jdbc.exceptions.CommunicationsException;
import com.mysql.cj.xdevapi.Column;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.ArrayStack;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.StringReader;
import java.net.SocketException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

@Component
@Slf4j
public class SqlExecutor {

    @Autowired(required = false) private DataSource dataSource;

    // 是否正在连接
    private AtomicBoolean connecting = new AtomicBoolean(true);

    private Connection connection;

    /**
     * 执行sql 语句 并返回一个对象
     */
    public Object executeSingleQuery(String sql) throws SQLException{
        if (dataSource == null) {
            return null;
        }
        if (!sql.trim().endsWith(";")) {
            sql += ";";
        }
        Connection conn = getConnection();
        if (conn == null) {
            return null;
        }
        try {
            ResultSet result = conn.createStatement().executeQuery(sql);
            ResultSetMetaData metaData = result.getMetaData();
            List<ColumnDescribe> columns = new ArrayList<>();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                columns.add(new ColumnDescribe(i, metaData.getColumnName(i),
                        metaData.getColumnTypeName(i),
                        metaData.getColumnType(i)));
            }
            List<Map<String, Object>> list = new ArrayList<>();
            if (result.next()) {
                Map<String, Object> data = new HashMap<>();
                for (int i = 0; i < columns.size(); i++) {
                    ColumnDescribe col = columns.get(i);
                    Object value = result.getObject(col.index);
                    data.put(col.name, value);
                }
                return data;
            }
            return null;
        }  catch (CommunicationsException ex){
            log.warn(ex.toString(), ex);
            reconnect();
            return executeQuery(sql);
        } catch (SQLException ex){
            log.warn(ex.toString(), ex);
            if(ex.getMessage().equals("connection disabled")){
                reconnect();
                return executeQuery(sql);
            }
            throw ex;
        }
    }

    /**
     * 建立数据库连接
     * @return
     */
    private Connection getConnection() {
        if(connection == null){
            synchronized (SqlExecutor.this){
                if(connection == null){
                    try {
                        connection = dataSource.getConnection();

                        // 开启心跳

                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
        return connection;
    }


    /**
     * 执行sql 语句 并返回一个数组
     */
    public Object executeQuery(String sql) throws SQLException  {
        if(dataSource == null){
            return null;
        }
        if(!sql.trim().endsWith(";")){
            sql += ";";
        }
//        Connection conn = dataSource.getConnection();
        Connection conn = getConnection();
        if(conn == null){
            return null;
        }
        try {
            ResultSet result = conn.createStatement().executeQuery(sql);
            ResultSetMetaData metaData = result.getMetaData();
            List<ColumnDescribe> columns = new ArrayList<>();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                columns.add(new ColumnDescribe(
                        i,
                        metaData.getColumnLabel(i),
                        metaData.getColumnLabel(i),
                        metaData.getColumnType(i)));
            }
            List<Map<String, Object>> list = new ArrayList<>();
            while (result.next()) {
                Map<String, Object> data = new HashMap<>();
                for (int i = 0; i < columns.size(); i++) {
                    ColumnDescribe col = columns.get(i);
                    Object value = result.getObject(col.index);
                    data.put(col.name, value);
                }
                list.add(data);
            }
            return list;
        } catch (CommunicationsException ex){
            log.warn(ex.toString(), ex);
            reconnect();
            return executeQuery(sql);
        } catch (SQLException ex){
            log.warn(ex.toString(), ex);
            if(ex.getMessage().equals("connection disabled")){
                reconnect();
                return executeQuery(sql);
            }
            throw ex;
        }
    }


    // TODO 数据库 重连有问题
    private synchronized void reconnect() {
//        if(connecting.getAndSet(true)){
//            return;
//        }
        connecting.set(true);
        try {
            if(connection != null){
                connection.close();
            }
            connection = null;
            connection = dataSource.getConnection();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            connection = null;
        } finally {
            connecting.set(false);
        }
    }


    /**
     * 列表描述
     */
    @Data
    class ColumnDescribe {

        private int index;

        private String name;

        private String typeName;

        private int type;

        public ColumnDescribe(int index, String name, String typeName, int type) {
            this.index = index;
            this.name = name;
            this.typeName = typeName;
            this.type = type;
        }
    }
}
